<script setup>
import * as Cesium from "cesium";
import { onMounted, ref } from "vue";
import RenderUtil from "../utils/RenderUtil";
import CustomPrimitive from "../utils/CustomPrimitive";
import MyPrimitive from "../primitives/MyPrimitive";
// import {
//   CalculateSpeedShader,
//   UpdatePositionShader,
//   PostProcessingPositionShader,
//   segmentDrawVert,
//   fullscreenVert,
//   screenDrawFrag,
//   segmentDrawFrag,
//   trailDrawFrag,
// } from "@/assets/shader.min.js";
import * as Shader from "@/assets/shader.min.js";
import { windData } from "../assets/windData";
const {
  CalculateSpeedShader,
  UpdatePositionShader,
  PostProcessingPositionShader,
  segmentDrawVert,
  fullscreenVert,
  screenDrawFrag,
  segmentDrawFrag,
  trailDrawFrag,
} = Shader.default;

let viewer, baseLayer, enumLayer;
const tianditutkArr = [
  "590447cdbdf97775b57588a69ebb903d",
  "75f1cc913e8d68f7bd4b6f6383f78ceb",
  "4c4c0f03b0bdb58811f657fec0c09eb5",
];
// ['590447cdbdf97775b57588a69ebb903d', '75f1cc913e8d68f7bd4b6f6383f78ceb', '4c4c0f03b0bdb58811f657fec0c09eb5']
const tianditutk =
  tianditutkArr[Math.floor(Math.random() * tianditutkArr.length)];
Cesium.Ion.defaultAccessToken =
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhZjE5ZTBhZC00YzE1LTQyMWYtOWM4MC00NmFlZjZhOGI3ZDciLCJpZCI6MTI4NTI2LCJpYXQiOjE2OTg4MzczOTN9.26EHXec7Ufty_B4e5_YWcB0t--zlquly7r66WVSS9wE";
// 设置Cesium默认视角
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
  108.4,
  21.3,
  129.3,
  48.6
);
// 计算通道Primitive
class ParticlesComputing {
  constructor(context, data, userInput, viewerParameters) {
    this.createWindTextures(context, data);
    this.createParticlesTextures(data, context, userInput, viewerParameters);
    this.createComputingPrimitives(data, userInput, viewerParameters);
  }

  createWindTextures(context, data) {
    var windTextureOptions = {
      context: context,
      width: data.dimensions.lon,
      height: data.dimensions.lat * data.dimensions.lev,
      pixelFormat: Cesium.PixelFormat.RED,
      pixelDatatype: Cesium.PixelDatatype.FLOAT,
      flipY: false,
      sampler: new Cesium.Sampler({
        // the values of texture will not be interpolated
        minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
        magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
      }),
    };

    this.windTextures = {
      U: RenderUtil.createTexture(windTextureOptions, data.U.array),
      V: RenderUtil.createTexture(windTextureOptions, data.V.array),
      W: RenderUtil.createTexture(windTextureOptions, data.W.array),
      H: RenderUtil.createTexture(windTextureOptions, data.H.array),
    };
  }

  // 先找一个随机的像素点,以此像素点经纬度范围生成随机位置
  getValidRange(data) {
    // 给定维度大小与边界：
    const dimensions = [
      data.dimensions.lon,
      data.dimensions.lat,
      data.dimensions.lev,
    ];
    const minimum = [data.lon.min, data.lat.min, data.lev.min];
    const maximum = [data.lon.max, data.lat.max, data.lev.max];
    // 计算边界与网格间距
    const interval = [
      (maximum[0] - minimum[0]) / (dimensions[0] - 1),
      (maximum[1] - minimum[1]) / (dimensions[1] - 1),
      dimensions[2] > 1 ? (maximum[2] - minimum[2]) / (dimensions[2] - 1) : 1.0,
    ];
    let id = Math.floor(Math.random() * data.U.array.length);

    // z = ⌊id / (L × M)⌋​
    let z = Math.floor(id / (dimensions[0] * dimensions[1]));
    // ​​r = id mod (L × M)​
    let left = id % (dimensions[0] * dimensions[1]);
    // ​​y = ⌊r / L⌋​
    let y = Math.floor(left / dimensions[0]);
    // ​​x = r mod L​
    let x = left % dimensions[0];

    // 网格假设：经纬度按​​线性间距​​、​​左包含​​的等距网格生成
    // 第 ​​x​​ 列对应区间 ​​[lon_min + x·d_lon, lon_min + (x+1)·d_lon)​​
    // 第 ​​y​​ 行对应 ​​[lat_min + y·d_lat, lat_min + (y+1)·d_lat)​​
    let lon = Cesium.Math.randomBetween(
      minimum[0] + x * interval[0],
      minimum[0] + (x + 1) * interval[0]
    );
    let lat = Cesium.Math.randomBetween(
      minimum[1] + (y - 1) * interval[1],
      minimum[1] + y * interval[1]
    );
    // let lev = Cesium.Math.randomBetween(minimum[2] + (z - 1) * interval[2], minimum[2] + z * interval[2])
    // 垂直值：​​lev = data.H.array[id]
    let lev = data.H.array[id] || 0;
    return [lon, lat, lev];
  }

  randomizeParticles(data, maxParticles, viewerParameters) {
    var array = new Float32Array(4 * maxParticles);
    for (var i = 0; i < maxParticles; i++) {
      let pos = this.getValidRange(data);
      array[4 * i] = pos[0];
      array[4 * i + 1] = pos[1];
      array[4 * i + 2] = pos[2];
      array[4 * i + 3] = 0.0;
    }
    return array;
  }

  createParticlesTextures(data, context, userInput, viewerParameters) {
    var particlesTextureOptions = {
      context: context,
      width: userInput.particlesTextureSize,
      height: userInput.particlesTextureSize,
      pixelFormat: Cesium.PixelFormat.RGBA,
      pixelDatatype: Cesium.PixelDatatype.FLOAT,
      flipY: false,
      sampler: new Cesium.Sampler({
        // the values of texture will not be interpolated
        minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
        magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
      }),
    };
    var particlesArray = this.randomizeParticles(
      data,
      userInput.maxParticles,
      viewerParameters
    );
    console.log('particlesArray', particlesArray)
    var zeroArray = new Float32Array(4 * userInput.maxParticles).fill(0);

    this.particlesTextures = {
      previousParticlesPosition: RenderUtil.createTexture(
        particlesTextureOptions,
        particlesArray
      ),
      currentParticlesPosition: RenderUtil.createTexture(
        particlesTextureOptions,
        particlesArray
      ),
      nextParticlesPosition: RenderUtil.createTexture(
        particlesTextureOptions,
        particlesArray
      ),
      postProcessingPosition: RenderUtil.createTexture(
        particlesTextureOptions,
        particlesArray
      ),

      particlesSpeed: RenderUtil.createTexture(
        particlesTextureOptions,
        zeroArray
      ),
    };
  }

  destroyParticlesTextures() {
    Object.keys(this.particlesTextures).forEach((key) => {
      this.particlesTextures[key].destroy();
    });
  }

  createComputingPrimitives(data, userInput, viewerParameters) {
    const dimension = new Cesium.Cartesian3(
      data.dimensions.lon,
      data.dimensions.lat,
      data.dimensions.lev
    );
    const minimum = new Cesium.Cartesian3(
      data.lon.min,
      data.lat.min,
      data.lev.min
    );
    const maximum = new Cesium.Cartesian3(
      data.lon.max,
      data.lat.max,
      data.lev.max
    );
    const interval = new Cesium.Cartesian3(
      (maximum.x - minimum.x) / (dimension.x - 1),
      (maximum.y - minimum.y) / (dimension.y - 1),
      dimension.z > 1 ? (maximum.z - minimum.z) / (dimension.z - 1) : 1.0
    );
    const lonRange = new Cesium.Cartesian2(data.lon.min, data.lon.max);
    const latRange = new Cesium.Cartesian2(data.lat.min, data.lat.max);
    const uSpeedRange = new Cesium.Cartesian2(data.U.min, data.U.max);
    const vSpeedRange = new Cesium.Cartesian2(data.V.min, data.V.max);
    const wSpeedRange = new Cesium.Cartesian2(data.W.min, data.W.max);

    const that = this;

    this.primitives = {
      calculateSpeed: new CustomPrimitive({
        commandType: "Compute",
        uniformMap: {
          U: function () {
            return that.windTextures.U;
          },
          V: function () {
            return that.windTextures.V;
          },
          W: function () {
            return that.windTextures.W;
          },
          currentParticlesPosition: function () {
            return that.particlesTextures.currentParticlesPosition;
          },
          dimension: function () {
            return dimension;
          },
          minimum: function () {
            return minimum;
          },
          maximum: function () {
            return maximum;
          },
          interval: function () {
            return interval;
          },
          uSpeedRange: function () {
            return uSpeedRange;
          },
          vSpeedRange: function () {
            return vSpeedRange;
          },
          wSpeedRange: function () {
            return wSpeedRange;
          },
          speedScaleFactor: function () {
            return viewerParameters.pixelSize * userInput.speedFactor;
          },
        },
        fragmentShaderSource: new Cesium.ShaderSource({
          sources: [CalculateSpeedShader],
        }),
        outputTexture: this.particlesTextures.particlesSpeed,
        preExecute: function () {
          // swap textures before binding
          if (userInput !== undefined && userInput.dynamic) {
            var temp;
            temp = that.particlesTextures.previousParticlesPosition;
            that.particlesTextures.previousParticlesPosition =
              that.particlesTextures.currentParticlesPosition;
            that.particlesTextures.currentParticlesPosition =
              that.particlesTextures.postProcessingPosition;
            that.particlesTextures.postProcessingPosition = temp;
          }

          // keep the outputTexture up to date
          that.primitives.calculateSpeed.commandToExecute.outputTexture =
            that.particlesTextures.particlesSpeed;
        },
      }),

      updatePosition: new CustomPrimitive({
        commandType: "Compute",
        uniformMap: {
          currentParticlesPosition: function () {
            return that.particlesTextures.currentParticlesPosition;
          },
          particlesSpeed: function () {
            return that.particlesTextures.particlesSpeed;
          },
        },
        fragmentShaderSource: new Cesium.ShaderSource({
          sources: [UpdatePositionShader],
        }),
        outputTexture: this.particlesTextures.nextParticlesPosition,
        preExecute: function () {
          // keep the outputTexture up to date
          that.primitives.updatePosition.commandToExecute.outputTexture =
            that.particlesTextures.nextParticlesPosition;
        },
      }),

      postProcessingPosition: new CustomPrimitive({
        commandType: "Compute",
        uniformMap: {
          nextParticlesPosition: function () {
            return that.particlesTextures.nextParticlesPosition;
          },
          particlesSpeed: function () {
            return that.particlesTextures.particlesSpeed;
          },
          viewerLonRange: function () {
            return viewerParameters.lonRange;
          },
          viewerLatRange: function () {
            return viewerParameters.latRange;
          },
          lonRange: function () {
            return lonRange;
          },
          latRange: function () {
            return latRange;
          },
          dimension: function () {
            return dimension;
          },
          minimum: function () {
            return minimum;
          },
          maximum: function () {
            return maximum;
          },
          interval: function () {
            return interval;
          },
          H: function () {
            return that.windTextures.H;
          },
          randomCoefficient: function () {
            var randomCoefficient = Math.random();
            return randomCoefficient;
          },
          dropRate: function () {
            return userInput.dropRate;
          },
          dropRateBump: function () {
            return userInput.dropRateBump;
          },
        },
        fragmentShaderSource: new Cesium.ShaderSource({
          sources: [PostProcessingPositionShader],
        }),
        outputTexture: this.particlesTextures.postProcessingPosition,
        preExecute: function () {
          // keep the outputTexture up to date
          that.primitives.postProcessingPosition.commandToExecute.outputTexture =
            that.particlesTextures.postProcessingPosition;
        },
      }),
    };
  }
}
// 主通道Primitive
class ParticlesRendering {
  constructor(
    context,
    data,
    userInput,
    viewerParameters,
    particlesComputing,
    colour
  ) {
    this.colour = colour === "height" ? true : false;
    this.createRenderingTextures(context, data);
    this.createRenderingFramebuffers(context);
    this.createRenderingPrimitives(
      context,
      data,
      userInput,
      viewerParameters,
      particlesComputing
    );
  }

  createRenderingTextures(context, data) {
    const colorTextureOptions = {
      context: context,
      width: context.drawingBufferWidth,
      height: context.drawingBufferHeight,
      pixelFormat: Cesium.PixelFormat.RGBA,
      pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
    };
    const depthTextureOptions = {
      context: context,
      width: context.drawingBufferWidth,
      height: context.drawingBufferHeight,
      pixelFormat: Cesium.PixelFormat.DEPTH_COMPONENT,
      pixelDatatype: Cesium.PixelDatatype.UNSIGNED_INT,
    };

    const colorTableTextureOptions = {
      context: context,
      width: data.colorTable.colorNum,
      height: 1,
      pixelFormat: Cesium.PixelFormat.RGB,
      pixelDatatype: Cesium.PixelDatatype.FLOAT,
      sampler: new Cesium.Sampler({
        minificationFilter: Cesium.TextureMinificationFilter.LINEAR,
        magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR,
      }),
    };

    this.textures = {
      segmentsColor: RenderUtil.createTexture(colorTextureOptions),
      segmentsDepth: RenderUtil.createTexture(depthTextureOptions),

      currentTrailsColor: RenderUtil.createTexture(colorTextureOptions),
      currentTrailsDepth: RenderUtil.createTexture(depthTextureOptions),

      nextTrailsColor: RenderUtil.createTexture(colorTextureOptions),
      nextTrailsDepth: RenderUtil.createTexture(depthTextureOptions),
      colorTable: RenderUtil.createTexture(
        colorTableTextureOptions,
        data.colorTable.array
      ),
    };
  }

  createRenderingFramebuffers(context) {
    this.framebuffers = {
      segments: RenderUtil.createFramebuffer(
        context,
        this.textures.segmentsColor,
        this.textures.segmentsDepth
      ),
      currentTrails: RenderUtil.createFramebuffer(
        context,
        this.textures.currentTrailsColor,
        this.textures.currentTrailsDepth
      ),
      nextTrails: RenderUtil.createFramebuffer(
        context,
        this.textures.nextTrailsColor,
        this.textures.nextTrailsDepth
      ),
    };
  }

  createSegmentsGeometry(userInput) {
    const repeatVertex = 4;
    // 坐标系
    //  z
    //  | /y
    //  |/
    //  o------x
    var st = []; // 纹理数组 st坐标系，左下角被定义为(0,0), 右上角为(1,1)，用于传入到顶点着色器中指代粒子的位置
    for (var s = 0; s < userInput.particlesTextureSize; s++) {
      for (var t = 0; t < userInput.particlesTextureSize; t++) {
        for (var i = 0; i < repeatVertex; i++) {
          st.push(s / userInput.particlesTextureSize);
          st.push(t / userInput.particlesTextureSize);
        }
      }
    }
    st = new Float32Array(st);

    var normal = []; //法向数组
    // it is not normal itself, but used to control lines drawing
    const pointToUse = [-1, 1];
    const offsetSign = [-1, 1];
    for (var i = 0; i < userInput.maxParticles; i++) {
      normal.push(
        // (point to use, offset sign, not used component)
        -1,
        -1,
        0,
        -1,
        1,
        0,
        1,
        -1,
        0,
        1,
        1,
        0
      );
    }
    normal = new Float32Array(normal);

    var vertexIndexes = []; // 索引,一个粒子矩形由两个三角形组成
    for (var i = 0, vertex = 0; i < userInput.maxParticles; i++) {
      vertexIndexes.push(
        // 第一个三角形用的顶点
        vertex + 0,
        vertex + 1,
        vertex + 2,
        // 第二个三角形用的顶点
        vertex + 2,
        vertex + 1,
        vertex + 3
      );

      vertex += repeatVertex;
    }
    vertexIndexes = new Uint32Array(vertexIndexes);
    var geometry = new Cesium.Geometry({
      attributes: new Cesium.GeometryAttributes({
        st: new Cesium.GeometryAttribute({
          componentDatatype: Cesium.ComponentDatatype.FLOAT,
          componentsPerAttribute: 2,
          values: st,
        }),
        normal: new Cesium.GeometryAttribute({
          componentDatatype: Cesium.ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: normal,
        }),
      }),
      indices: vertexIndexes,
    });

    return geometry;
  }

  createRenderingPrimitives(
    context,
    data,
    userInput,
    viewerParameters,
    particlesComputing
  ) {
    const that = this;
    this.primitives = {
      segments: new CustomPrimitive({
        commandType: "Draw",
        attributeLocations: {
          st: 0, // When true, the vertex has a 2D texture coordinate attribute.
          // 32-bit floating-point. 2 components per attribute
          normal: 1, // When true, the vertex has a normal attribute (normalized), which is commonly used for lighting.
          // 32-bit floating-point. 3 components per attribute.
        },
        geometry: this.createSegmentsGeometry(userInput),
        primitiveType: Cesium.PrimitiveType.TRIANGLES,
        uniformMap: {
          previousParticlesPosition: function () {
            return particlesComputing.particlesTextures
              .previousParticlesPosition;
          },
          currentParticlesPosition: function () {
            return particlesComputing.particlesTextures
              .currentParticlesPosition;
          },
          postProcessingPosition: function () {
            return particlesComputing.particlesTextures.postProcessingPosition;
          },
          particlesSpeed: function () {
            return particlesComputing.particlesTextures.particlesSpeed;
          },
          colorTable: function () {
            return that.textures.colorTable;
          },
          aspect: function () {
            return context.drawingBufferWidth / context.drawingBufferHeight;
          },
          H: function () {
            return data.H.array;
          },
          hRange: function () {
            return new Cesium.Cartesian2(data.H.min, data.H.max);
          },
          uSpeedRange: function () {
            return new Cesium.Cartesian2(data.U.min, data.U.max);
          },
          vSpeedRange: function () {
            return new Cesium.Cartesian2(data.V.min, data.V.max);
          },
          wSpeedRange: function () {
            return new Cesium.Cartesian2(data.W.min, data.W.max);
          },
          pixelSize: function () {
            return viewerParameters.pixelSize;
          },
          lineWidth: function () {
            return userInput.lineWidth;
          },
          particleHeight: function () {
            return userInput.particleHeight;
          },
          colour: function () {
            return that.colour;
          },
        },
        vertexShaderSource: new Cesium.ShaderSource({
          sources: [segmentDrawVert],
        }),
        fragmentShaderSource: new Cesium.ShaderSource({
          sources: [segmentDrawFrag],
        }),
        rawRenderState: RenderUtil.createRawRenderState({
          // undefined value means let Cesium deal with it
          viewport: undefined,
          depthTest: {
            enabled: true,
          },
          depthMask: true,
        }),
        framebuffer: this.framebuffers.segments,
        autoClear: true,
      }),

      trails: new CustomPrimitive({
        commandType: "Draw",
        attributeLocations: {
          position: 0, // When true, the vertex has a 3D position attribute.
          // 64-bit floating-point (for precision). 3 components per attribute.
          st: 1,
        },
        geometry: RenderUtil.getFullscreenQuad(),
        primitiveType: Cesium.PrimitiveType.TRIANGLES,
        uniformMap: {
          segmentsColorTexture: function () {
            return that.textures.segmentsColor;
          },
          segmentsDepthTexture: function () {
            return that.textures.segmentsDepth;
          },
          currentTrailsColor: function () {
            return that.framebuffers.currentTrails.getColorTexture(0);
          },
          trailsDepthTexture: function () {
            return that.framebuffers.currentTrails.depthTexture;
          },
          fadeOpacity: function () {
            return userInput.fadeOpacity;
          },
        },
        // prevent Cesium from writing depth because the depth here should be written manually
        vertexShaderSource: new Cesium.ShaderSource({
          defines: ["DISABLE_GL_POSITION_LOG_DEPTH"],
          sources: [fullscreenVert],
        }),
        fragmentShaderSource: new Cesium.ShaderSource({
          defines: ["DISABLE_LOG_DEPTH_FRAGMENT_WRITE"],
          sources: [trailDrawFrag],
        }),
        rawRenderState: RenderUtil.createRawRenderState({
          viewport: undefined,
          depthTest: {
            enabled: true,
            func: Cesium.DepthFunction.ALWAYS, // always pass depth test for full control of depth information
          },
          depthMask: true,
        }),
        framebuffer: this.framebuffers.nextTrails,
        autoClear: true,
        preExecute: function () {
          // swap framebuffers before binding
          if (userInput.dynamic) {
            var temp;
            temp = that.framebuffers.currentTrails;
            that.framebuffers.currentTrails = that.framebuffers.nextTrails;
            that.framebuffers.nextTrails = temp;
          }

          // keep the framebuffers up to date
          that.primitives.trails.commandToExecute.framebuffer =
            that.framebuffers.nextTrails;
          that.primitives.trails.clearCommand.framebuffer =
            that.framebuffers.nextTrails;
        },
      }),

      screen: new CustomPrimitive({
        commandType: "Draw",
        attributeLocations: {
          position: 0,
          st: 1,
        },
        geometry: RenderUtil.getFullscreenQuad(),
        primitiveType: Cesium.PrimitiveType.TRIANGLES,
        uniformMap: {
          trailsColorTexture: function () {
            return that.framebuffers.nextTrails.getColorTexture(0);
          },
          trailsDepthTexture: function () {
            return that.framebuffers.nextTrails.depthTexture;
          },
        },
        // prevent Cesium from writing depth because the depth here should be written manually
        vertexShaderSource: new Cesium.ShaderSource({
          defines: ["DISABLE_GL_POSITION_LOG_DEPTH"],
          sources: [fullscreenVert],
        }),
        fragmentShaderSource: new Cesium.ShaderSource({
          defines: ["DISABLE_LOG_DEPTH_FRAGMENT_WRITE"],
          sources: [screenDrawFrag],
        }),
        rawRenderState: RenderUtil.createRawRenderState({
          viewport: undefined,
          depthTest: {
            enabled: false,
          },
          depthMask: true,
          blending: {
            enabled: true,
          },
        }),
        framebuffer: undefined, // undefined value means let Cesium deal with it
      }),
    };
  }
}
// 创建自定义全球风场Primitive
function createCustomPrimitive() {
  // 默认用户输入数据
  const userInput = {
    maxParticles: 250000,
    particleHeight: 1000,
    fadeOpacity: 0.95,
    dropRate: 0.003,
    dropRateBump: 0.01,
    speedFactor: 0.5,
    lineWidth: 4,
    dynamic: true,
    particlesTextureSize: 500,
  };
  const viewerParameters = {
    lonRange: new Cesium.Cartesian2(),
    latRange: new Cesium.Cartesian2(),
    pixelSize: 0.0,
    lonDisplayRange: new Cesium.Cartesian2(),
    latDisplayRange: new Cesium.Cartesian2(),
  };
  const globeBoundingSphere = new Cesium.BoundingSphere(
    Cesium.Cartesian3.ZERO,
    0.99 * 6378137.0
  );
  updateViewerParameters(windData);
  function updateViewerParameters(data) {
    // Cesium的 computeViewRectangle()直接返回视图矩形，但未处理极地扩展和经度环形问题，因此需要自己实现
    var viewRectangle = viewer.camera.computeViewRectangle(
      viewer.scene.globe.ellipsoid
    );
    var lonLatRange = RenderUtil.viewRectangleToLonLatRange(viewRectangle);
    viewerParameters.lonRange.x = Math.max(lonLatRange.lon.min, data.lon.min);
    viewerParameters.lonRange.y = Math.min(lonLatRange.lon.max, data.lon.max);
    viewerParameters.latRange.x = Math.max(lonLatRange.lat.min, data.lat.min);
    viewerParameters.latRange.y = Math.min(lonLatRange.lat.max, data.lat.max);

    var pixelSize = viewer.camera.getPixelSize(
      globeBoundingSphere,
      viewer.scene.drawingBufferWidth,
      viewer.scene.drawingBufferHeight
    );

    if (pixelSize > 0) {
      viewerParameters.pixelSize = pixelSize;
    }
  }
  console.log("windData", windData);
  console.log("viewerParameters", viewerParameters);
  const particlesComputing = new ParticlesComputing(
    viewer.scene.context,
    windData,
    userInput,
    viewerParameters
  );
  const particlesRendering = new ParticlesRendering(
    viewer.scene.context,
    windData,
    userInput,
    viewerParameters,
    particlesComputing,
    "speed"
  );
  console.log("particlesComputing", particlesComputing);
  console.log("particlesRendering", particlesRendering);
  const primitives = [
    particlesComputing.primitives.calculateSpeed,
    particlesComputing.primitives.updatePosition,
    particlesComputing.primitives.postProcessingPosition,
    particlesRendering.primitives.segments,
    particlesRendering.primitives.trails,
    particlesRendering.primitives.screen,
  ];
  for (let primitive of primitives) {
    viewer.scene.primitives.add(primitive);
  }
}
// 测试数据
function testData() {
  let data = {
    U: {
      array: new Float32Array([
        -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
      ]),
    },
    H: {
      array: new Float32Array([
        -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
      ]),
    },
    dimensions: {
      lon: 100,
      lat: 50,
      lev: 10,
    },
    lon: {
      min: 0,
      max: 359.5,
    },
    lat: {
      min: 0,
      max: 90,
    },
    lev: {
      min: 0,
      max: 1000,
    },
  };
  function getValidRange(data) {
    // 给定维度大小与边界：
    const dimensions = [
      data.dimensions.lon,
      data.dimensions.lat,
      data.dimensions.lev,
    ];
    const minimum = [data.lon.min, data.lat.min, data.lev.min];
    const maximum = [data.lon.max, data.lat.max, data.lev.max];
    // 计算边界与网格间距
    const interval = [
      (maximum[0] - minimum[0]) / (dimensions[0] - 1),
      (maximum[1] - minimum[1]) / (dimensions[1] - 1),
      dimensions[2] > 1 ? (maximum[2] - minimum[2]) / (dimensions[2] - 1) : 1.0,
    ];
    console.log("interval", interval)
    let id = Math.floor(Math.random() * data.U.array.length);

    // z = ⌊id / (L × M)⌋​
    let z = Math.floor(id / (dimensions[0] * dimensions[1]));
    // ​​r = id mod (L × M)​
    let left = id % (dimensions[0] * dimensions[1]);
    // ​​y = ⌊r / L⌋​
    let y = Math.floor(left / dimensions[0]);
    // ​​x = r mod L​
    let x = left % dimensions[0];
    console.log("id, z, left, y, x", id, z, left, y, x)

    // 网格假设：经纬度按​​线性间距​​、​​左包含​​的等距网格生成
    // 第 ​​x​​ 列对应区间 ​​[lon_min + x·d_lon, lon_min + (x+1)·d_lon)​​
    // 第 ​​y​​ 行对应 ​​[lat_min + y·d_lat, lat_min + (y+1)·d_lat)​​
    let lon = Cesium.Math.randomBetween(
      minimum[0] + x * interval[0],
      minimum[0] + (x + 1) * interval[0]
    );
    let lat = Cesium.Math.randomBetween(
      minimum[1] + (y - 1) * interval[1],
      minimum[1] + y * interval[1]
    );
    // let lev = Cesium.Math.randomBetween(minimum[2] + (z - 1) * interval[2], minimum[2] + z * interval[2])
    // 垂直值：​​lev = data.H.array[id]
    let lev = data.H.array[id] || 0;
    return [lon, lat, lev];
  }
  const range = getValidRange(data)
  console.log("range", range)
}
onMounted(() => {
  testData()
  baseLayer = new Cesium.ImageryLayer(
    new Cesium.WebMapTileServiceImageryProvider({
      url:
        "http://t0.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=" +
        tianditutk,
      layer: "tdtImgLayer",
      style: "default",
      format: "image/jpeg",
      tileMatrixSetID: "GoogleMapsCompatible", //使用谷歌的瓦片切片方式
      show: true,
    })
  );
  viewer = new Cesium.Viewer("cesiumContainer", {
    // animation: false, // 动画小组件
    baseLayerPicker: false, // 底图组件，选择三维数字地球的底图
    fullscreenButton: false, // 全屏组件
    vrButton: false, // VR模式
    geocoder: false, // 地理编码搜索组件
    homeButton: false, // 首页，点击之后将视图跳转到默认视角
    infoBox: false, // 信息框
    sceneModePicker: false, // 场景模式，切换2D、3D和Columbus Views（CV）模式
    selectionIndicator: false, // 是否显示选取指示器组件
    // timeline: false, // 时间轴
    navigationHelpButton: false, // 帮助提示，如何操作数字地球
    navigationInstructionsInitiallyVisible: false,
    // terrain: new Cesium.Terrain.fromWorldTerrain({
    //   url: Cesium.IonResource.fromAssetId(3956),
    //   requestWaterMask: true,    // 请求水体效果所需要的海水波浪数据
    //   requestVertexNormals: true   // 请求地形照明数据
    // }),
    baseLayer,
    contextOptions: {
      requestWebgl2: true, // 开启webgl2渲染
    },
  });

  enumLayer = new Cesium.ImageryLayer(
    new Cesium.WebMapTileServiceImageryProvider({
      url:
        "http://t0.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=" +
        tianditutk,
      layer: "tdtCiaLayer",
      style: "default",
      format: "image/jpeg",
      tileMatrixSetID: "GoogleMapsCompatible", //使用谷歌的瓦片切片方式
      show: true,
    })
  );
  viewer.imageryLayers.add(enumLayer);
  // 显示帧率
  viewer.scene.debugShowFramesPerSecond = true;
  window.baseLayer = baseLayer;
  window.viewer = viewer;
  window.enumLayer = enumLayer;
  // 添加自定义全球风场Primitive
  createCustomPrimitive();
});
</script>

<template>
  <div id="cesiumContainer"></div>
</template>

<style lang="scss">
#cesiumContainer {
  width: 100vw;
  height: 100vh;
  position: relative;

  .cesium-viewer-bottom {
    visibility: hidden;
  }
}
</style>
